<?php
namespace Matrix;

/**
 * IoC容器
 *
 * IoC容器用来管理创建对象, 可以用来创建复杂的对象, 并且支持单例模式
 * 可以使应用更加灵活, 更加具有可测试性
 *
 * 注册一个对象:
 * <code>
 * IoC::register('mysql', function() {
 * 		return mysql_connect('host', 'user', 'pass');
 * });
 * </code>
 *
 * 注册一个单例对象:
 * <code>
 * IoC::singleton('mysql', function() {
 * 		return mysql_connect('host', 'user', 'pass');
 * });
 * </code>
 * 也可以这样注册一个单例模式的对象:
 * <code>
 * IoC::instance('mysql', mysql_connect('host', 'user', 'pass'));
 * </code>
 *
 * 解析对象:
 * <code>
 * $mailer = IoC::resolve('mysql');
 * </code>
 */
class IoC
{
	/**
	 * 注册管理器
	 *
	 * @var array
	 */
	public static $registry = array();
	
	/**
	 * 已经实例化了的单例
	 *
	 * @var array
	 */
	public static $singletons = array();
	
	/**
	 * 判定一个对象是否已经被注册
	 *
	 * @param	string	$name
	 * @return	boolean
	 */
	public static function registered($name)
	{
		return isset(static::$registry[$name]);
	}
	
	/**
	 * 注册一个对象和它的解析器
	 *
	 * @param	string	$name
	 * @param	mixed	$resolver
	 * @param	boolean	$singleton
	 * @return	void
	 */
	public static function register($name, $resolver = null, $singleton = false)
	{
		if ($resolver === null) {
			$resolver = $name;
		}
		static::$registry[$name] = compact('resolver', 'singleton');
	}
	
	/**
	 * 注册一个单例对象
	 *
	 * @param	string	$name
	 * @param	mixed	$resolver
	 * @return	void
	 */
	public static function singleton($name, $resolver = null)
	{
		static::register($name, $resolver, true);
	}
	
	/**
	 * 注册一个已经存在的实例
	 *
	 * @param	string	$name
	 * @param	mixed	$instance
	 * @return	void
	 */
	public static function instance($name, $instance)
	{
		static::$singletons[$name] = $instance;
	}
	
	/**
	 * 解析指定的类型
	 *
	 * @param	string	$type
	 * @param	array	$parameters
	 * @return	mixed
	 */
	public static function resolve($type, $parameters = array())
	{
		if (isset(static::$singletons[$type])) {
			return static::$singletons[$type];
		}
		
		if (isset(static::$registry[$type])) {
			$concrete = array_get(static::$registry[$type], 'resolver', $type);
		} else {
			$concrete = $type;
		}
		if ($concrete == $type || $concrete instanceof \Closure) {
			$object = static::build($concrete, $parameters);
		} else {
			$object = static::resolve($concrete, $parameters);
		}
		if (isset(static::$registry[$type]['singleton'])) {
			static::$singletons[$type] = $object;
		}
		return $object;
	}
	
	/**
	 * 实例化指定的类型
	 *
	 * @param	string	$type
	 * @param	array	$parameters
	 * @return	mixed
	 */
	protected static function build($type, $parameters = array())
	{
		if ($type instanceof \Closure) {
			return call_user_func_array($type, $parameters);
		}
		$reflector = new \ReflectionClass($type);
		if (!$reflector->isInstantiable()) {
			throw new \Exception("Resolution target [$type] is not instantiable.");
		}
		$constructor = $reflector->getConstructor();
		if ($constructor === null) {
			return new $type;
		}
		$dependencies = static::dependencies($constructor->getParameters());
		return $reflector->newInstanceArgs($dependencies);
	}
	
	/**
	 * 解决反射参数的所有依耐性
	 *
	 * @param	array	$parameters
	 * @return	array
	 */
	protected static function dependencies($parameters)
	{
		$dependencies = array();
		foreach ($parameters as $parameter) {
			$dependency = $parameter->getClass();
			if (is_null($dependency)) {
				throw new Exception("Unresolvable dependency resolving [$parameter].");
			}
			$dependencies[] = static::resolve($dependency->name);
		}
		return (array)$dependencies;
	}
}